package com.mokairui.threadpool;

import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.concurrent.TimeUnit;

/**
 * @Description 自定义线程池
 * @Author Mokairui
 * @Since 2021/10/6
 */
@Slf4j(topic = "c.threadPool")
public class ThreadPool {
    // 任务队列
    private BlockingQueue<Runnable> taskQueue;

    // 线程集合
    private HashSet<Worker> workers = new HashSet<>();

    // 核心线程数
    private int coreSize;

    // 获取任务的超时时间
    private long timeout;
    private TimeUnit timeUnit;

    private RejectPolicy<Runnable> rejectPolicy;

    public ThreadPool(int coreSize,
                      long timeout,
                      TimeUnit timeUnit,
                      int queueCapacity,
                      RejectPolicy rejectPolicy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.rejectPolicy = rejectPolicy;
    }

    // 执行任务
    public void execute(Runnable task) {
        // 当任务数没有超过 coreSize 时, 直接交给 worker 对象执行
        if (workers.size() < coreSize) {
            Worker worker = new Worker(task);
            log.debug("新增 worker{}, {}", worker, task);
            workers.add(worker);
            worker.start();
        } else {
            log.debug("加入任务队列{}", task);
            // 超过了 coreSize 时, 加入任务队列暂存
//            taskQueue.put(task);

            // 如果遇到队列满了呢? 可能会有很多中情况如:
            // 1. 死等
//             queue.put(task);
            // 2. 带超时等待
//            queue.offer(task, 500, TimeUnit.MILLISECONDS);
            // 3. 让调用者放弃任务执行
//            什么都不写就是放弃执行任务
            // 4. 让调用者抛出异常
//            throw new RuntimeException("任务执行失败")
            // 5. 让调用者自己执行任务
//            task.run();
            // 如果我们都在这里定义好那么会出现很多的if else 同时不好维护,
            // 所以这里可以使用策略模式, 将选择抛给线程池的使用者
            taskQueue.tryPut(rejectPolicy, task);
        }
    }

    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            // 执行任务
            // 1. 当 task 不为空, 执行任务
            // 2. 当 task 执行完毕, 再接着从任务队列获取任务并执行
//            while (task != null || (task = taskQueue.take()) != null) {
            while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
                try {
                    log.debug("正在执行...", task);
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            synchronized (workers) {
                log.debug("worker 被移除{}", this);
                workers.remove(this);
            }
        }
    }

    public static void main(String[] args) {
        ThreadPool threadPool = new ThreadPool(2,
                1000,
                TimeUnit.MILLISECONDS,
                10,
                ((queue, task) -> {
                    // 1. 死等
                    queue.put(task);
                }));
        for (int i = 0; i < 5; i++) {
            int j = i;
            threadPool.execute(() -> {
                log.debug("{}", j);
            });
        }
    }
}

@FunctionalInterface // 拒接策略
interface RejectPolicy<T> {
    void reject(BlockingQueue<T> queue, T task);
}
